import os
import sys

import requests
import json
import time
import configparser
import logging
from pathlib import Path
from typing import Optional, Dict, Any, Union

from utils.AesUtils import AesUtils
from utils.ConfigHelper import ConfigHelper


class ApiClient:
    """
    API 请求帮助类（支持配置文件和错误日志记录）

    配置文件示例 (./config/apiconfig.ini):
    [api]
    base_url = https://api.example.com
    username = your_username
    password = your_password

    功能：
    - 从配置文件读取 API 配置
    - 支持基本认证和 Token 认证
    - 错误日志记录到 error.log
    - 详细的请求日志
    - 自动重试机制
    """

    def __init__(
            self,
            config_file: Optional[str] = None,  # 改为可选参数
            section: str = "api",
            timeout: int = 30,
            max_retries: int = 3,
            retry_delay: float = 1.0,
            log_errors: bool = True,
    ) -> object:
        """
        初始化 API 客户端

        :param config_file: 配置文件路径
        :param section: 配置文件中 API 配置的节名
        :param timeout: 请求超时时间(秒)
        :param max_retries: 最大重试次数
        :param retry_delay: 重试延迟(秒)
        :param log_errors: 是否记录错误日志
        """
        # 如果没有提供config_file，则使用默认路径
        if config_file is None:
            config_file = self._get_default_config_path()

        # 读取配置文件
        config = ConfigHelper(config_file)
        # 读取配置文件
        self.config = config
        self.base_url = config.get_value(section, 'base_url').rstrip("/")
        self.auth_url = config.get_value(section, 'auth_url').rstrip("/")
        self.student_url = config.get_value(section, 'student_url').rstrip("/")
        self.upload_result_info = config.get_value(section, 'upload_result_info').rstrip("/")
        self.username = config.get_value(section, 'username')
        self.password = config.get_value(section, 'password')
        self.grant_type = config.get_value(section, 'grant_type')
        self.auth_token = config.get_value(section, 'auth_token')
        self.basic_token = config.get_value(section, 'basic_token')
        self.client_no = config.get_value(section, 'client_no')
        self.cloud_url = config.get_value(section, 'cloud_url')
        self.fpl_student_url = config.get_value(section, 'fpl_student_url')
        self.fpl_student_up_count = config.get_value(section, 'fpl_student_up_count')

        # 请求设置
        self.timeout = timeout
        self.max_retries = max_retries
        self.retry_delay = retry_delay
        self.session = requests.Session()

        # 设置默认请求头
        self.default_headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
        }

        # 初始化日志系统
        self._setup_logging(log_errors)

    def _get_default_config_path(self):
        """获取默认配置文件路径"""
        if getattr(sys, 'frozen', False):
            # 打包后exe的目录
            base_dir = os.path.dirname(sys.executable)
        else:
            # 开发环境中的目录
            base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

        return os.path.join(base_dir, "config", "apiconfig.ini")

    def _load_config(self, config_file: str, section: str) -> Dict[str, str]:
        """加载配置文件"""
        config = configparser.ConfigParser()
        try:
            if not Path(config_file).exists():
                raise FileNotFoundError(f"配置文件 {config_file} 不存在")

            config.read(config_file)
            if section not in config:
                raise ValueError(f"配置文件中缺少 [{section}] 节")

            return dict(config[section])
        except Exception as e:
            raise ValueError(f"加载配置文件失败: {e}")

    def _setup_logging(self, log_errors: bool) -> None:
        """设置日志系统"""
        # 创建日志目录
        log_dir = Path("logs")
        log_dir.mkdir(exist_ok=True)

        # 配置基础日志
        logging.basicConfig(
            level=logging.INFO,
            format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
            handlers=[
                logging.StreamHandler(),  # 控制台输出
            ]
        )
        # 定义日志目录路径（改为相对路径或确保绝对路径存在）
        log_dir = "logs"  # 项目根目录下的logs文件夹
        error_log_path = os.path.join(log_dir, "error.log")
        # 添加错误日志文件处理器
        if log_errors:
            error_handler = logging.FileHandler(
                filename=error_log_path,
                mode="a",
                encoding="utf-8",
            )
            error_handler.setLevel(logging.ERROR)
            error_handler.setFormatter(
                logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
            )
            logging.getLogger().addHandler(error_handler)

        self.logger = logging.getLogger(__name__)

    def _request(
            self,
            method: str,
            host: str,
            endpoint: str,
            params: Optional[Dict[str, Any]] = None,
            data: Optional[Union[Dict[str, Any], str, bytes]] = None,
            json_data: Optional[Dict[str, Any]] = None,
            headers: Optional[Dict[str, str]] = None,
            files: Optional[Dict[str, Any]] = None,
            **kwargs,
    ) -> requests.Response:
        """
        执行 HTTP 请求

        :param method: HTTP 方法 (GET/POST/PUT/DELETE等)
        :param endpoint: API 端点
        :param params: 查询参数
        :param data: 请求体数据
        :param json_data: JSON 请求体
        :param headers: 请求头
        :param files: 上传的文件
        :param kwargs: 其他 requests 参数
        :return: requests.Response 对象
        :raises: ApiRequestError 当请求失败时
        """
        url = f"{host}/{endpoint.lstrip('/')}"
        request_headers = self.default_headers.copy()
        if headers:
            request_headers.update(headers)

        # 记录请求日志
        self._log_request(method, url, params, data, json_data, request_headers)

        # 认证方式：优先使用 Token，其次使用基本认证
        auth = None
        if self.auth_token:
            request_headers["Authorization"] = f"Bearer {self.auth_token}"
        # elif self.username and self.password:
        #     auth = (self.username, self.password)

        # 重试机制
        last_exception = None
        for attempt in range(self.max_retries + 1):
            try:
                response = self.session.request(
                    method=method,
                    url=url,
                    params=params,
                    data=data,
                    json=json_data,
                    headers=request_headers,
                    files=files,
                    timeout=self.timeout,
                    auth=auth,
                    **kwargs,
                )

                # 记录响应日志
                self._log_response(response)

                # 检查状态码
                if not response.ok:
                    error_msg = f"API request failed with status {response.status_code}"
                    self.logger.error(f"{error_msg}\nURL: {url}\nResponse: {response.text}")
                    raise ApiRequestError(error_msg, response=response)

                return response

            except (requests.exceptions.RequestException, json.JSONDecodeError) as e:
                last_exception = e
                if attempt < self.max_retries:
                    delay = self.retry_delay * (attempt + 1)
                    self.logger.warning(f"请求失败，第 {attempt + 1} 次重试 (等待 {delay} 秒)...")
                    time.sleep(delay)
                    continue

                error_msg = f"API request failed after {self.max_retries} retries"
                self.logger.error(f"{error_msg}\nURL: {url}\nError: {str(e)}")
                raise ApiRequestError(error_msg) from last_exception

    def _authenticate(self):
        """获取访问令牌"""
        try:
            headers = {
                "Authorization": "Basic " + self.basic_token,
                "Content-Type": "application/x-www-form-urlencoded"
            }

            params = {
                "grant_type": self.grant_type,
                "username": self.username + "@" + self.client_no,
                "password": self.password
            }

            response = self.postParams(
                f"{self.base_url}",
                f"{self.auth_url}",
                params=params,
                headers=headers
            )
            print(response)
            print(response.get("data"))
            if 1 == response.get("code"):
                error_msg = f"认证失败: {response.code} - {response.msg}"
                self.logger.error(error_msg)
                raise ApiRequestError(error_msg, response=response)
            token_data = response.get("data")
            print(token_data)
            self.siteCode = token_data.get('siteCode')
            self.siteName = token_data.get('siteName')
            self.type = token_data.get('type')
            self.encryptKey = token_data.get('encryptKey')
            self.token = token_data.get('token')
            self.typeValue = token_data.get('typeValue')
            # expires_in = token_data.get('expires_in', 3600)
            # self.token_expires_at = time.time() + expires_in - 60  # 提前1分钟过期
            self.logger.info("认证成功，获取到访问令牌")
        except Exception as e:
            self.logger.error(f"认证过程中发生错误: {str(e)}")
            raise

    def _log_request(
            self,
            method: str,
            url: str,
            params: Optional[Dict[str, Any]],
            data: Optional[Union[Dict[str, Any], str, bytes]],
            json_data: Optional[Dict[str, Any]],
            headers: Dict[str, str],
    ) -> None:
        """记录请求日志"""
        log_msg = f"\n[API Request] {method} {url}"
        log_msg += f"\nHeaders: {headers}"

        if params:
            log_msg += f"\nParams: {params}"
        if data and not isinstance(data, (str, bytes)):
            log_msg += f"\nData: {data}"
        if json_data:
            log_msg += f"\nJSON: {json_data}"

        self.logger.info(log_msg)

    def _log_response(self, response: requests.Response) -> None:
        """记录响应日志"""
        try:
            content = response.json()
            content_str = json.dumps(content, indent=2, ensure_ascii=False)
        except ValueError:
            content_str = response.text[:200] + ("..." if len(response.text) > 200 else "")

        log_msg = f"\n[API Response] {response.status_code} {response.reason}"
        log_msg += f"\nHeaders: {dict(response.headers)}"
        log_msg += f"\nContent: {content_str}"

        self.logger.info(log_msg)

    def get(
            self,
            host: str,
            endpoint: str,
            params: Optional[Dict[str, Any]] = None,
            headers: Optional[Dict[str, str]] = None,
            **kwargs,
    ) -> Union[Dict[str, Any], str]:
        """执行 GET 请求"""
        response = self._request("GET", host, endpoint, params=params, headers=headers, **kwargs)
        return self._parse_response(response)

    def post(
            self,
            host: str,
            endpoint: str,
            data: Optional[Union[Dict[str, Any], str, bytes]] = None,
            json_data: Optional[Dict[str, Any]] = None,
            headers: Optional[Dict[str, str]] = None,
            files: Optional[Dict[str, Any]] = None,
            **kwargs,
    ) -> Union[Dict[str, Any], str]:
        """执行 POST 请求"""
        response = self._request(
            "POST",
            host,
            endpoint,
            data=data,
            json_data=json_data,
            headers=headers,
            files=files,
            **kwargs,
        )
        return self._parse_response(response)

    def postParams(
            self,
            host: str,
            endpoint: str,
            params: Optional[Dict[str, Any]] = None,
            data: Optional[Union[Dict[str, Any], str, bytes]] = None,
            json_data: Optional[Dict[str, Any]] = None,
            headers: Optional[Dict[str, str]] = None,
            files: Optional[Dict[str, Any]] = None,
            **kwargs,
    ) -> Union[Dict[str, Any], str]:
        """执行 POST 请求"""
        response = self._request(
            "POST",
            host,
            endpoint,
            params=params,
            data=data,
            json_data=json_data,
            headers=headers,
            files=files,
            **kwargs,
        )
        return self._parse_response(response)

    def put(
            self,
            endpoint: str,
            data: Optional[Union[Dict[str, Any], str, bytes]] = None,
            json_data: Optional[Dict[str, Any]] = None,
            headers: Optional[Dict[str, str]] = None,
            **kwargs,
    ) -> Union[Dict[str, Any], str]:
        """执行 PUT 请求"""
        response = self._request(
            "PUT", endpoint, data=data, json_data=json_data, headers=headers, **kwargs
        )
        return self._parse_response(response)

    def delete(
            self,
            endpoint: str,
            headers: Optional[Dict[str, str]] = None,
            **kwargs,
    ) -> Union[Dict[str, Any], str]:
        """执行 DELETE 请求"""
        response = self._request("DELETE", endpoint, headers=headers, **kwargs)
        return self._parse_response(response)

    def _parse_response(self, response: requests.Response) -> Union[Dict[str, Any], str]:
        """解析响应内容"""
        content_type = response.headers.get("Content-Type", "")
        if "application/json" in content_type:
            try:
                return response.json()
            except ValueError:
                return response.text
        return response.text


class ApiRequestError(Exception):
    """API 请求异常"""

    def __init__(self, message: str, response: Optional[requests.Response] = None):
        super().__init__(message)
        self.response = response
        self.status_code = response.status_code if response else None
        self.response_text = response.text if response else None

    def __str__(self):
        if self.response is not None:
            return f"{super().__str__()} (Status: {self.status_code}, Response: {self.response_text[:200]})"
        return super().__str__()


# 使用示例
if __name__ == "__main__":
    try:
        # 创建 API 客户端实例
        api = ApiClient(config_file="../config/apiconfig.ini")

        # GET 请求示例
        print("=== GET 请求示例 获取访问令牌 ===")
        api._authenticate()
        print(f"siteCode：" + api.siteCode)
        print(f"siteName：" + api.siteName)
        print(f"type：" + api.type)
        print(f"encryptKey：" + api.encryptKey)
        print(f"token：" + api.token)
        print(f"typeValue：" + api.typeValue)

        # POST 请求示例
        print("\n=== POST 请求示例 考生信息下载 ===")
        response = api.post(
            api.student_url,
            json_data={
                "data": {
                    "itemCode": "",
                    "pageNo": 1
                },
                "msEquipment": "",
                "requestTime": 0,
                "encrypt": "0",
                "version": "1.0.0",
                "lastUpdateTime": "",
                "token": "cf0b6154-41ec-4aed-98e9-8c29bc7269b5"
            },
            headers={
                "Authorization": "Bearer " + api.token
            }
        )
        print(response)
        print(response.get("data"))
        if 1 == response.get("code"):
            error_msg = f"考生信息查询失败: {response.code} - {response.msg}"
            api.logger.error(error_msg)
            raise ApiRequestError(error_msg, response=response)
        token_data = response.get("data")
        # 创建 AES 加密器
        encryptKey = api.encryptKey.encode('utf-8')
        aes = AesUtils(encryptKey)
        print("考生加密信息:", token_data)
        # 解密（输入 HEX）
        decrypted_text = aes.decrypt(token_data)
        print("解密结果:", decrypted_text)

    except Exception as e:
        print(f"初始化 API 客户端失败: {e}")
